#pragma once

#include "Business/Business.h"
#include "StatusMachine/StatusMachine.h"
#include "Business/ClassicArbitrageContracts.h"
#include "Business/CustomerPosition.h"
#include "Business/Band.h"
#include "Business/DealingHistory.h"
#include "Business/PendingMarket.h"

class Customer;
class MarketStorage;
class QueueContext;
class DealingRepository;
class CancellationRepository;
class ContractMatcher;

class OneLegArbitrageBusiness;


namespace OneLegArbitrageBusinessMachine {
	struct MajorMarket
	{
		MajorMarket(Direction::TYPE d,int p,int q)
			:direction(d),price(p),quantity(q)
		{
		}
		Direction::TYPE direction;
		int price;
		int quantity;
	};

	class Event
	{
	public:
		enum TYPE {NONE,MARKET_EVENT,MAJOR_MARKET_EVENT,PENDING_MARKET_EVENT, DEALING_EVENT,TIME_EVENT,START_COMMAND, EDIT_COMMAND,CLEAR_COMMAND,STOP_COMMAND};
		explicit Event(TYPE type = NONE);
		explicit Event(PendingMarket const& pendingMarket);
		explicit Event(MajorMarket const& majorMarkt);
		explicit Event(Market const& market);
		explicit Event(Dealing const& dealing);
		int GetType() const { return m_type; }
		PendingMarket const& GetPendingMarket() const {return *(PendingMarket*)m_data;}
		MajorMarket const& GetMajorMarket() const {return *(MajorMarket*)m_data;}
		Market const& GetMarket() const { return *(Market*)m_data; }
		Dealing const& GetDealing() const { return *(Dealing*)m_data; }

	private:
		int m_type;
		char m_data[1024];
	};

	typedef Status<OneLegArbitrageBusiness,Event> StatusT;
	class StatusBase : public StatusT
	{
	public:
		StatusBase(std::string const& name, OneLegArbitrageBusiness& context):StatusT(name,context){}
	private:
		virtual void DoBefore();
	};
	typedef ConditionBase<OneLegArbitrageBusiness,Event> ConditionT;
	typedef StatusMachine<OneLegArbitrageBusiness,Event> StatusMachineT;


#define DEFINE_CONDITION(condition_name)						\
	class condition_name : public ConditionT					\
	{															\
	public:														\
	explicit condition_name(OneLegArbitrageBusiness& context)	\
	:ConditionT(context){}										\
	virtual ~condition_name() {}								\
	private:													\
	virtual bool DoCheck(Event const& event);					\
	}

	DEFINE_CONDITION(HasChanceCondition);
	DEFINE_CONDITION(MinorOrderedCondition);
	DEFINE_CONDITION(MinorAllCancelledCondition);
	DEFINE_CONDITION(MinorOpenedCondition);
	DEFINE_CONDITION(MinorOffsetOrderedCondition);
	DEFINE_CONDITION(MinorCompleteOffsetCondition);
	DEFINE_CONDITION(StopMarketCondition);
	DEFINE_CONDITION(MinorOffsetCancellationSuccessCondition);

	DEFINE_CONDITION(StartCondition);
	DEFINE_CONDITION(EditCondition);
	DEFINE_CONDITION(StopCondition);

#undef DEFINE_CONDITION

#define DEFINE_STATUS(class_name,name)						\
	class class_name : public StatusBase					\
	{														\
	public:													\
	explicit class_name(OneLegArbitrageBusiness& context)	\
	:StatusBase(name,context){}								\
	virtual ~class_name(){}									\
	private:												\
	virtual void DoExecute(Event const& event);				\
	}

	DEFINE_STATUS(StartStatus,"start");
	DEFINE_STATUS(MinorOpenSentStatus,"minor open sent");
	DEFINE_STATUS(MinorOpenCancellationSentStatus,"minor open cancellation sent");
	DEFINE_STATUS(OpenedStatus,"opened");
	DEFINE_STATUS(MinorOffsetOrderedStatus,"minor offset ordered");
	DEFINE_STATUS(MinorOffsetCancellationSentStatus,"minor offset cancellation sent");
	DEFINE_STATUS(MinorStopSentStatus,"minor stop sent");
	DEFINE_STATUS(EditableStatus,"editable");
	DEFINE_STATUS(StopStatus,"stop");

#undef DEFINE_STATUS
}

#include "Business/PriceChangeChecker.h"

class OneLegArbitrageArguemnt
{
public:
	OneLegArbitrageArguemnt(ArbitrageContracts const& contracts,
		int waitMarketCount,int maxTime,int priceChangeCount)
		:m_contracts(contracts),m_waitMarketCount(waitMarketCount),
		m_maxTime(maxTime),m_priceChangeCount(priceChangeCount)
	{
	}
	
	int GetMajor() const {return m_contracts.GetMajor();}
	int GetMinor() const {return m_contracts.GetMinor();}
	int GetFarther() const {return m_contracts.GetFarther();}
	int GetNearby() const {return m_contracts.GetNearby();}
	int GetWaitMarketCount() const { return m_waitMarketCount; }
	int GetMaxTime() const { return m_maxTime;}
	int GetPriceChangeCount() const { return m_priceChangeCount; }
private:
	ArbitrageContracts m_contracts;
	int m_waitMarketCount;
	int m_maxTime;
	int m_priceChangeCount;
};

struct Order;
class Cancellation;

class OneLegArbitrageBusinessSnapshot 
{
public:
	OneLegArbitrageBusinessSnapshot(char const* status,Direction::TYPE direction,
		int minorQuantity,double profit)
		:m_status(status),m_direction(direction),
		m_minorQuantity(minorQuantity),m_profit(profit)
	{
	}
	char const* GetStatus() const {return m_status;}
	Direction::TYPE GetDirection() const {return m_direction;}
	int GetMinorQuantity() const {return m_minorQuantity;}
	double GetProfit() const {return m_profit;}

	bool operator==(OneLegArbitrageBusinessSnapshot const& other) const
	{
		return m_status == other.m_status && m_direction == other.m_direction &&
			m_minorQuantity == other.m_minorQuantity;
	}
	bool operator!=(OneLegArbitrageBusinessSnapshot const& other) const
	{
		return !operator==(other);
	}
private:
	char const * m_status;
	Direction::TYPE m_direction;
	int m_minorQuantity;
	double m_profit;
};

class OneLegArbitrageBusiness : public AutoUpdateUIBusiness<OneLegArbitrageBusiness>
{
public:
	typedef OneLegArbitrageArguemnt Arguemnt;
	typedef OneLegArbitrageBusinessSnapshot Snapshot;

public:
	OneLegArbitrageBusiness(Customer& customer,
		OneLegArbitrageArguemnt const& argument,
		MarketStorage const& marketStorage,
		QueueContext& queueContext,
		DealingRepository& dealingRepository,
		CancellationRepository& cancellationRepository);
	virtual ~OneLegArbitrageBusiness();

	OneLegArbitrageArguemnt const& GetArgument() const { return m_argument; }

	void Init();
	void OpenMinor();
	void CancelMinorOpen();
	void OffsetMinor();
	void CancelMinorOffset();
	void ClearMinor();

	bool HasChance(OneLegArbitrageBusinessMachine::MajorMarket const& pendingMarket);
	bool IsStopMarket() const;

	OneLegArbitrageBusinessSnapshot GetSnapshot() const;
	void UpdateUI(OneLegArbitrageBusinessSnapshot const& snapshot);


	char const* GetMajorCode() const;
	char const* GetMinorCode() const;
	PositionValue const& GetMinorPosition() const { return m_minorPosition; }

private:
	virtual void OnBefore() {}
	virtual void OnAfter() {}

	virtual void DoOnInit();;
	virtual void DoOnMarket(Market const& market);
	virtual void DoOnPendingMarket(PendingMarket const& pendingMarket);

	int GetMajorBuy1()
	{
		if (m_majorPendingMarkets.GetBuys().empty())
		{
			return 0;
		}
		return m_majorPendingMarkets.GetBuys().rbegin()->first;
	}

	int GetMajorSell1()
	{
		if (m_majorPendingMarkets.GetSells().empty())
		{
			return 0;
		}
		return m_majorPendingMarkets.GetSells().begin()->first;
	}
	virtual void DoOnDealing(Dealing const& dealing);
	virtual void DoOnTimeout(RequestTimeout const& requestTimeout);
	virtual void DoOnTrade(Trade const& trade);
	virtual void DoOnCommand(Command const& command);
	virtual void DoOnExchangeStatus(int exchangeStatus);

	Dealing& PushOrder(Order const& order);
	Cancellation& PushCancellation(Cancellation const& cancellation);

	double GetMinorOpenPrice(Direction::TYPE direction) const;
	double GetMinorOffsetPrice() const;

	void UpdateArgument(OneLegArbitrageArguemnt const& argument);

	bool IsEditable() const
	{
		return strcmp(m_machine->GetCurrentStatus()->GetName().c_str(), "editable") == 0;
	}


	OneLegArbitrageArguemnt m_argument;
	MarketStorage const& m_marketStorage;
	QueueContext& m_queueContext;
	DealingRepository& m_dealingRepository;
	CancellationRepository& m_cancellationRepository;
	PendingMarkets m_majorPendingMarkets;
	PendingMarkets m_minorPendingMarkets;

	OneLegArbitrageBusinessMachine::StatusMachineT* m_machine;

	Direction::TYPE m_direction;

	PriceChangeChecker m_priceChangeChecker;

	PositionValue m_minorPosition;
	DealingHistory m_minorOpenHistory;
	DealingHistory m_minorOffsetHistory;
};